﻿//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

using King.Text;
using King.Extensions;
using King.Reflection;

namespace King.TeamFoundation {

    public class TfOptionAttribute : Attribute { }
    public class TfTargetAttribute : Attribute {
        private int m_order;
        public TfTargetAttribute(int order) {
            m_order = order;
        }
        public int Order { get { return m_order; } }
        public bool IsUserContent { get; set; }
    }

    public abstract class Command : TextNode {

        private string m_exe;

        public Command(string exe) {
            m_exe = exe;
        }

        public string WorkingDirectory { get; set; }
        public string RedirectTarget { get; set; }

        private object GetOptionValue(PropertyInfo propertyInfo) {
            var value = propertyInfo.GetValue(this);
            if (value == null)
                return null;

            var name = "/" + propertyInfo.Name.ToLower();

            if (propertyInfo.PropertyType == typeof(object))
                value = TextGenerator.ReadToEnd(value);

            var type = value.GetType();
            if (type == typeof(bool)) {
                if ((bool)value == false)
                    return null;

                return name;
            }

            if (type.IsEnum)
                value = value.ToString().ToLower();

            return new TextJoin(":") { name, value.ToString() };
        }

        protected override IEnumerable<object> Content() {
            var userContent = GetType()
                .Get<PropertyInfo>(isInstance: true, isPublic: true)
                .SingleOrDefault(o =>
                    o.IsDefined<TfTargetAttribute>() &&
                    o.GetAttribute<TfTargetAttribute>().IsUserContent);

            if (userContent == null)
                return null;

            return new object[] { userContent.GetValue(this) };
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            if (!context.ContainsContent(this))
                return null;

            //var process = new TProcess(m_exe) {
            //    WorkingDirectory = WorkingDirectory,
            //    RedirectTarget = RedirectTarget,
            //    Arguments = new TextJoin(" ") {
            //        // command name
            //        GetType().Name.SplitCamelCase().Skip(1).StringJoin().ToLower(),

            //        // switches
            //        from o in GetType().GetInstancePublic<PropertyInfo>()
            //        where o.IsDefined<TfOptionAttribute>()
            //        select GetOptionValue(o),

            //        // targets
            //        from o in GetType().GetInstancePublic<PropertyInfo>()
            //        let target = o.GetAttribute<TfTargetAttribute>()
            //        where target != null
            //        orderby target.Order
            //        select o.GetValue(this)
            //    }
            //};

            //process.OnError += OnError;

            //return new[] { process };
            throw new NotImplementedException();
        }

        public event Func<string, bool> OnError;
    }
    public abstract class TfCommand : Command {

        public TfCommand()
            : base("tf.exe") {
        }
    }
    public abstract class TfptCommand : Command {

        public TfptCommand()
            : base("tfpt.exe") {
        }
    }

    public enum TfLock {
        None,
        Checkin,
        Checkout,
    }
    public enum TfFormat {
        Brief,
        Detailed
    }
    public enum TfSort {
        Ascending,
        Descending,
    }
    public enum TfPermission {
        Private,
        Public,
        PublicLimited,
    }
    public enum TfChild {
        Replace,
        Merge,
    }

    public class TfVersionSpec : TextNode {

        protected override IEnumerable<object> Children(TextNodeContext context) {

            if (DateTime != null)
                yield return new TextNodes() { "D", DateTime.ToString() };

            if (Changeset != null)
                yield return new TextNodes() { "C", DateTime.ToString() };

            if (Label != null)
                yield return new TextNodes() { "L", DateTime.ToString() };

            if (Workspace != null)
                yield return new TextNodes() { "W", DateTime.ToString() };

            yield return "T";
        }

        public DateTime? DateTime { get; set; }
        public int? Changeset { get; set; }
        public string Label { get; set; }
        public string Workspace { get; set; }
    }
    public class TfNote : TextNode {

        private object m_name;
        private object m_value;

        public TfNote(object name, object value) {
            m_name = name;
            m_value = value;
        }

        protected override IEnumerable<object> Children(TextNodeContext context) {
            return new TextJoin("=") { 
                new TextNodes() { "\"", m_name, "\"" },
                new TextNodes() { "\"", m_value, "\"" },
            };
        }
    }

    public class TfGet : TfCommand {

        [TfTarget(1)]
        public object Itemspec { get; set; }

        [TfOption]
        public bool All { get; set; }

        [TfOption]
        public bool Overwrite { get; set; }

        [TfOption]
        public bool Force { get; set; }

        [TfOption]
        public bool Preview { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public bool Remap { get; set; }

        [TfOption]
        public bool NoPrompt { get; set; }

        [TfOption]
        public string Login { get; set; }
    }
    public class TfAdd : TfCommand {

        [TfTarget(1, IsUserContent = true)]
        public object Itemspec { get; set; }

        [TfOption]
        public bool NoPrompt { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public object Login { get; set; }
    }
    public class TfRename : TfCommand {

        [TfTarget(1)]
        public object OldItem { get; set; }

        [TfTarget(2)]
        public object NewItem { get; set; }

        [TfOption]
        public TfLock? Lock { get; set; }

        [TfOption]
        public object Login { get; set; }
    }
    public class TfBranch : TfCommand {

        [TfTarget(1)]
        public object OldItem { get; set; }

        [TfTarget(2)]
        public object NewItem { get; set; }

        [TfOption]
        public object Version { get; set; }

        [TfOption]
        public bool NoGet { get; set; }

        [TfOption]
        public bool NoPrompt { get; set; }

        [TfOption]
        public bool Silent { get; set; }

        [TfOption]
        public bool Checkin { get; set; }

        [TfOption]
        public object Comment { get; set; }

        [TfOption]
        public object Name { get; set; }

        [TfOption]
        public object Login { get; set; }
    }
    public class TfMerge : TfCommand {

        [TfTarget(1)]
        public object Source { get; set; }

        [TfTarget(2)]
        public object Destination { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public bool Force { get; set; }

        [TfOption]
        public bool Candidate { get; set; }

        [TfOption]
        public bool Discard { get; set; }

        [TfOption]
        public object Version { get; set; }

        [TfOption]
        public TfLock? Lock { get; set; }

        [TfOption]
        public bool Preview { get; set; }

        [TfOption]
        public bool Baseless { get; set; }

        [TfOption]
        public bool NoSummary { get; set; }

        [TfOption]
        public bool NoImplicitBaseless { get; set; }

        [TfOption]
        public bool Conservative { get; set; }

        [TfOption]
        public TfFormat? Format { get; set; }

        [TfOption]
        public bool NoPrompt { get; set; }

        [TfOption]
        public bool Login { get; set; }
    }
    public class TfDelete : TfCommand {

        [TfTarget(1)]
        public object Itemspec { get; set; }

        [TfOption]
        public TfLock? Lock { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public object Login { get; set; }
    }
    public class TfWorkspace : TfCommand {

        [TfTarget(1)]
        public object WorkspaceName { get; set; }

        [TfOption]
        public bool New { get; set; }

        [TfOption]
        public bool Delete { get; set; }

        [TfOption]
        public bool NoPrompt { get; set; }

        [TfOption]
        public object Template { get; set; }

        [TfOption]
        public object Computer { get; set; }

        [TfOption]
        public object Comment { get; set; }

        [TfOption]
        public object Collection { get; set; }

        [TfOption]
        public TfPermission? Permission { get; set; }

        [TfOption]
        public object Login { get; set; }
    }
    public class TfWorkspaces : TfCommand {

        [TfTarget(1)]
        public object WorkspaceName { get; set; }

        [TfOption]
        public object Owner { get; set; }

        [TfOption]
        public object Computer { get; set; }

        [TfOption]
        public object Collection { get; set; }

        [TfOption]
        public object Format { get; set; }

        [TfOption]
        public object UpdateUserName { get; set; }

        [TfOption]
        public object UpdateComputerName { get; set; }

        [TfOption]
        public object Login { get; set; }

        [TfOption]
        public object Remove { get; set; }
    }
    public class TfWorkfold : TfCommand {

        [TfTarget(1)]
        public object ServerFolder { get; set; }

        [TfTarget(2)]
        public object TargetFolder { get; set; }

        [TfOption]
        public object Login { get; set; }

        [TfOption]
        public object Workspace { get; set; }

        [TfOption]
        public object Collection { get; set; }

        [TfOption]
        public bool Map { get; set; }

        [TfOption]
        public bool UnMap { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public bool Cloak { get; set; }

        [TfOption]
        public bool Decloack { get; set; }
    }
    public class TfCheckout : TfCommand {

        public TfCheckout() {
            OnError += OpendForEditElsewhereWarning;
        }

        private bool OpendForEditElsewhereWarning(string error) {

            var lines = error.StringSplit(Environment.NewLine).ToQueue();
            string line = lines.Dequeue();

            //$/king/private/user/cking/lab2/dev/src/SRSolutions/AppNetwork/Network/Network.csproj:
            //   opened for edit in A1046;kcholera
            while (Regex.IsMatch(line, "^[$].*:$")) {
                while (line.Any() && Regex.IsMatch(line = lines.Dequeue(), "   opened for edit in")) {
                    if (lines.None())
                        return true;
                }
            }

            return false;
        }

        [TfTarget(1, IsUserContent = true)]
        public object Itemspec { get; set; }

        [TfOption]
        public TfLock? Lock { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public object Type { get; set; }

        [TfOption]
        public object Login { get; set; }
    }
    public class TfCheckin : TfCommand {

        [TfOption]
        public object Author { get; set; }

        [TfOption]
        public object Comment { get; set; }

        [TfOption]
        public bool NoPrompt { get; set; }

        [TfOption]
        public object Notes { get; set; }

        [TfOption]
        public object Override { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public bool Saved { get; set; }

        [TfOption]
        public bool Validate { get; set; }

        [TfOption]
        public bool Bypass { get; set; }

        [TfOption]
        public bool Force { get; set; }

        [TfOption]
        public object Login { get; set; }

        [TfOption]
        public object Shelveset { get; set; }

        [TfOption]
        public object Collection { get; set; }
    }
    public class TfHistory : TfCommand {

        [TfTarget(1)]
        public object Itemspec { get; set; }

        [TfOption]
        public object Version { get; set; }

        [TfOption]
        public int? StopAfter { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public object UserName { get; set; }

        [TfOption]
        public TfFormat? Format { get; set; }

        [TfOption]
        public bool SlotMode { get; set; }

        [TfOption]
        public bool ItemMode { get; set; }

        [TfOption]
        public bool NoPrompt { get; set; }

        [TfOption]
        public object Login { get; set; }

        [TfOption]
        public TfSort? Sort { get; set; }

        [TfOption]
        public object Collection { get; set; }
    }
    public class TfLabel : TfCommand {

        [TfTarget(1)]
        public object Labelname { get; set; }

        [TfTarget(2)]
        public object Itemspec { get; set; }

        [TfOption]
        public object Version { get; set; }

        [TfOption]
        public object Owner { get; set; }

        [TfOption]
        public object Comment { get; set; }

        [TfOption]
        public TfChild Child { get; set; }

        [TfOption]
        public bool Delete { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public object Login { get; set; }

        [TfOption]
        public object Collection { get; set; }
    }
    public class TfLabels : TfCommand {

        [TfTarget(1)]
        public object Labelname { get; set; }

        [TfOption]
        public object Owner { get; set; }

        [TfOption]
        public TfFormat Format { get; set; }

        [TfOption]
        public object Login { get; set; }

        [TfOption]
        public object Collection { get; set; }
    }
    public class TfRollback : TfCommand {
        //TF - Team Foundation Version Control Tool, Version 10.0.30319.1
        //Copyright (c) Microsoft Corporation.  All rights reserved.

        //Rolls back the changes in a single or a range of changesets:
        //tf rollback /changeset:changesetfrom~changesetto [itemspec] [/recursive]
        //            [/lock:none|checkin|checkout] [/version:versionspec]
        //            [/keepmergehistory] [/noprompt] [/login:username,[password]]

        //tf rollback /toversion:versionspec itemspec [/recursive]
        //            [/lock:none|checkin|checkout] [/version:versionspec]
        //            [/keepmergehistory] [/noprompt] [/login:username,[password]]

        //Versionspec:
        //    Date/Time         D"any .Net Framework-supported format"
        //                      or any of the date formats of the local machine
        //    Changeset number  Cnnnnnn
        //    Label             Llabelname
        //    Latest version    T
        //    Workspace         Wworkspacename;workspaceowner

        [TfTarget(1)]
        public object Itemspec { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public bool KeepMergeHistory { get; set; }

        [TfOption]
        public object ToVersion { get; set; }

        [TfOption]
        public object Changeset { get; set; }

        [TfOption]
        public TfLock? Lock { get; set; }

        [TfOption]
        public object Version { get; set; }

        [TfOption]
        public bool NoPrompt { get; set; }

        [TfOption]
        public bool Login { get; set; }
    }
    public class TfDir : TfCommand {

        [TfTarget(1, IsUserContent = true)]
        public object Itemspec { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public bool Folders { get; set; }

        [TfOption]
        public bool Deleted { get; set; }

        [TfOption]
        public string Login { get; set; }

        [TfOption]
        public string Collection { get; set; }
    }
    public class TfStatus : TfCommand {

        [TfTarget(1)]
        public object Itemspec { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public string Login { get; set; }

        [TfOption]
        public string User { get; set; }

        [TfOption]
        public string Workspace { get; set; }

        [TfOption]
        public TfFormat Format { get; set; }

        [TfOption]
        public string Collection { get; set; }
    }

    public class TfptScorch : TfptCommand {

        public TfptScorch() {
            OnError += delegate(string error) {
                return error.Contains("No inconsistencies found.");
            };
        }

        [TfTarget(1)]
        public object Itemspec { get; set; }

        [TfOption]
        public bool Recursive { get; set; }

        [TfOption]
        public string Exclude { get; set; }

        [TfOption]
        public int? BatchSize { get; set; }

        [TfOption]
        public bool NoPrompt { get; set; }

        [TfOption]
        public bool Deletes { get; set; }

        [TfOption]
        public bool Diff { get; set; }
    }


    public class BisSubscribeOptionAttribute : Attribute {
    }
    public enum DeliveryType {
        EmailHtml,
        EmailPlaintext,
        Soap
    }
    public class BisSubscribe : TextNode {

        public static string BisSubscribeExe = "bisSubscribe.exe";

        protected override IEnumerable<object> Content() {
            return ArbitraryContent;
        }
        protected override IEnumerable<object> Children(TextNodeContext context) {
            //var process = new TProcess(BisSubscribeExe) {
            //    Arguments = new TextNode(" ") {
            //        Unsubscribe ? "/unsubscribe" : null,

            //        // switches
            //        from o in GetType().GetInstancePublic<PropertyInfo>()
            //        where o.IsDefined<BisSubscribeOptionAttribute>()
            //        select GetOptionValue(o),
            //    }
            //};

            //process.OnError += OnError;

            //return new[] { process };
            throw new NotImplementedException();
        }

        private object GetOptionValue(PropertyInfo propertyInfo) {
            var value = propertyInfo.GetValue(this);
            if (value == null)
                return null;

            var name = "/" + propertyInfo.Name.ToLower();

            var result = new TextJoin(" ") { 
                name, 
                new TCommandLineArgument() {
                    value.ToString() 
                }
            }.ReadToEnd();

            return result;
        }

        public event Func<string, bool> OnError;

        [BisSubscribeOption]
        public string EventType { get; set; }

        [BisSubscribeOption]
        public string Filter { get; set; }

        [BisSubscribeOption]
        public string Address { get; set; }

        [BisSubscribeOption]
        public string Server { get; set; }

        [BisSubscribeOption]
        public string Collection { get; set; }

        [BisSubscribeOption]
        public DeliveryType DeliveryType { get; set; }

        [BisSubscribeOption]
        public int? Id { get; set; }

        public bool Unsubscribe { get; set; }
    }

}